#include "SelectPoller.h"
#include <algorithm>
#include <poll.h>
#include <sstream>
#include <sys/select.h>
#include <vector>
#include "Channel.h"
#include "DateTime.h"
#include "PlatLogger.h"
#include "Util.h"
namespace xncc {
namespace foundation {
    SelectPoller::SelectPoller()  = default;
    SelectPoller::~SelectPoller() = default;

    void SelectPoller::addChannel(const std::shared_ptr<Channel>& chnl)
    {
        Poller::addChannel(chnl);
        const int fd  = chnl->fd();
        channels_[fd] = chnl;
        if ((chnl->events() & (POLLIN | POLLPRI)) != 0) {
            readList_.insert(fd);
        }
        if ((chnl->events() & POLLOUT) != 0) {
            writeList_.insert(fd);
        }
    }
    void SelectPoller::removeChannel(const std::shared_ptr<Channel>& chnl)
    {
        Poller::removeChannel(chnl);
        int fd = chnl->fd();
        if (unlikely(channels_.find(fd) == channels_.end())) {
            std::ostringstream oss;
            TRACE_ERROR << "remove channel, but channel not exist fd:"_s << fd;
            return;
        }
        assert(channels_[fd] == chnl);
        size_t n = channels_.erase(fd);
        (void)n;
        assert(n == 1);
        readList_.erase(fd);
        writeList_.erase(fd);
    }
    void SelectPoller::updateChannel(const std::shared_ptr<Channel>& chnl)
    {
        // 使用裸指针 update如果是pending函数，有可能 ch已经因为断开而被移除和析构了
        // 存在崩溃风险，调整为智能指针的原因
        int fd = chnl->fd();
        if (unlikely(channels_.find(fd) == channels_.end())) {
            TRACE_ERROR << "channel may be remove because of disconnect"_s;
            return;
        }
        assert(channels_[fd] == chnl);
        if ((chnl->events() & (POLLIN | POLLPRI)) != 0) {
            readList_.insert(fd);
        }
        else {
            readList_.erase(fd);
        }
        if ((chnl->events() & POLLOUT) != 0) {
            writeList_.insert(fd);
        }
        else {
            writeList_.erase(fd);
        }
    }
    void SelectPoller::loopOnce(int waitUs)
    {
        {
            const int waitMs = waitUs / 1000;
            if (waitMs > types::DateTime::MILLS_PER_SEC) {
                wait_ts_.tv_sec  = waitMs / types::DateTime::MILLS_PER_SEC;
                wait_ts_.tv_usec = (waitMs % types::DateTime::MILLS_PER_SEC) * types::DateTime::MICROS_PER_MILL;
            }
            else {
                wait_ts_.tv_sec  = 0;
                wait_ts_.tv_usec = waitMs * types::DateTime::MICROS_PER_MILL;
            }
        }

        fd_set fdRead;
        FD_ZERO(&fdRead);
        int nfd = 0;
        for (auto fd : readList_) {
            if (fd != -1) {
                nfd = std::max(fd, nfd);
                FD_SET(fd, &fdRead);
            }
        }
        fd_set fdWrite;
        FD_ZERO(&fdWrite);
        for (auto fd : writeList_) {
            if (fd != -1) {
                nfd = std::max(fd, nfd);
                FD_SET(fd, &fdWrite);
            }
        }

        int num        = ::select(nfd + 1, &fdRead, &fdWrite, nullptr, &wait_ts_);
        int savedErrno = errno;
        if (num > 0) {
            readRedayList_.clear();
            writeRedayList_.clear();
            for (auto fd : readList_) {
                if (FD_ISSET(fd, &fdRead)) {
                    readRedayList_.push_back(fd);
                }
            }
            for (auto fd : writeList_) {
                if (FD_ISSET(fd, &fdWrite)) {
                    writeRedayList_.push_back(fd);
                }
            }
            for (auto fd : readRedayList_) {
                auto iter = channels_.find(fd);
                if (iter != channels_.end()) {
                    iter->second->handleRead();
                    // 必须判断 比如wakeup fd 先处理了 删除了 后边的fd 会导致channels_[fd]空指针
                }
            }
            for (auto fd : writeRedayList_) {
                auto iter = channels_.find(fd);
                if (iter != channels_.end()) {
                    iter->second->handleWrite();
                }
                /// 由于handleRead 函数可能 会调用handleclose 在断开连接的时候移除 channel
                // 所以这个地方必须判断channel是不是还存在
            }
        }
        else if (num == 0) {  // tmo
        }
        else {
            // error
            if (savedErrno != EINTR) {
                errno = savedErrno;
                SYSTEM_ERROR << "SelectPoller::poll()"_s;
            }
        }
    }
}  // namespace foundation
}  // namespace xncc